package game;

import java.awt.BorderLayout;

import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.Point;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import game.Engine.GameEngine;
import game.Engine.Map.Map;
import game.Input.InputEngine;
import game.display.DisplayEngine;
import game.display.SoundController;
import game.menu.Menu;
import game.util.HighscoreEntry;

/** Contains the main controller that starts the game
 * 
 * @author DMEComputer
 *
 */

public class MainController 
{
	public static enum State 
	{
		GAME, MENU, TUTORIAL_MENU, HIGHSCORE_MENU, NULL,
	}

	public static GameEngine gameEngine;
	public static InputEngine inputEngine;
	public static DisplayEngine displayEngine;
	public static SoundController soundController;
	public static JFrame j;
	public static Cursor invisibleCursor;
	private static State state = State.NULL;
	public static String name;
   private static final DisplayMode POSSIBLE_MODES[] = 
	   {
      new DisplayMode(800, 600, 16, 0),
      new DisplayMode(800, 600, 32, 0),
      new DisplayMode(800, 600, 24, 0),
      new DisplayMode(800, 600, -1, 75),
      
      new DisplayMode(640, 480, 16, 0),
      new DisplayMode(640, 480, 32, 0),
      new DisplayMode(640, 480, 24, 0),
      
      new DisplayMode(1024, 768, 16, 0),
      new DisplayMode(1024, 768, 32, 0),
      new DisplayMode(1024, 768, 24, 0),
  };
	public static void main(String[] args) 
	{
		name = (String)JOptionPane.showInputDialog(j,"Enter Your Name", "User",JOptionPane.PLAIN_MESSAGE,null,null,"");
		if(name == null || name.equals(""))name = "User";
		inputEngine = new InputEngine();
		displayEngine = new DisplayEngine();
		soundController = new SoundController(inputEngine.getSounds(),inputEngine.getSoundManager());
		Menu.setupMainMenu();
		Menu.setupTutorialMenu();
		Menu.setupHighscore();
		gameEngine = new GameEngine(nextMap());
		DisplayMode displayMode = findFirstCompatibleMode(POSSIBLE_MODES);
		GraphicsEnvironment environment = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		GraphicsDevice device = environment.getDefaultScreenDevice();
		invisibleCursor = Toolkit.getDefaultToolkit().createCustomCursor(Toolkit.getDefaultToolkit().getImage(""), new Point(0,0), "invisible");
		j = new JFrame();
		state = State.MENU;
		j.addKeyListener(inputEngine);
		j.addMouseListener(inputEngine);
		j.addMouseMotionListener(inputEngine);
		j.addMouseWheelListener(inputEngine);
		j.setLayout(new BorderLayout());
		j.add(displayEngine, BorderLayout.CENTER);
		j.setUndecorated(true);
		j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		j.setVisible(true);
		j.pack();
		device.setFullScreenWindow(j);
		device.setDisplayMode(displayMode);
		displayEngine.displayMenu(Menu.mainMenu);
	}
	
	/** Find the first compatible mode that the game
	 * can be displayed on
	 * 
	 * @param modes different display modes
	 * @return the display modes that can work on the computer
	 */
	
   public static DisplayMode findFirstCompatibleMode(
         DisplayMode modes[])
     {
        GraphicsEnvironment environment =
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice device = environment.getDefaultScreenDevice();
         DisplayMode goodModes[] = device.getDisplayModes();
         
         for (int i = 0; i < modes.length; i++) 
         {
             for (int j = 0; j < goodModes.length; j++) 
             {
            	 if (displayModesMatch(modes[i], goodModes[j])) 
            	 {
                     return modes[i];
                 }
             }

         }
         
         for (int i = 0; i < modes.length; i++) 
         {
             for (int j = 0; j < goodModes.length; j++) 
             {
            	 if (displayModesMatchOld(modes[i], goodModes[j])) 
            	 {
                     return modes[i];
                 }
             }

         }

         return null;
     }
   
   /** Compares the different display modes
    * 
    * @param mode1 display mode
    * @param mode2 display mode
    * @return true if display modes match
    */
   
   public static boolean displayModesMatch(DisplayMode mode1,
         DisplayMode mode2)

     {
         if (mode1.getWidth() != mode2.getWidth() ||
             mode1.getHeight() != mode2.getHeight())
         {
             return false;
         }

         if (mode1.getBitDepth() != mode2.getBitDepth())
         {
             return false;
             //mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
             //mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
         }

         if (mode1.getRefreshRate() != mode2.getRefreshRate())
          {
              return false;
/*mode1.getRefreshRate() !=
             DisplayMode.REFRESH_RATE_UNKNOWN &&
             mode2.getRefreshRate() !=
             DisplayMode.REFRESH_RATE_UNKNOWN &&*/
          }

          return true;
     }
   
   /** Compares the different display modes
    * 
    * @param mode1 display mode
    * @param mode2 display mode
    * @return true if display modes match
    */
   
   public static boolean displayModesMatchOld(DisplayMode mode1,
	         DisplayMode mode2)

	     {
	         if (mode1.getWidth() != mode2.getWidth() ||
	             mode1.getHeight() != mode2.getHeight())
	         {
	             return false;
	         }

	         if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
		         mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
	        		 mode1.getBitDepth() != mode2.getBitDepth())
	         {
	             return false;

	         }

	         if (mode1.getRefreshRate() !=
		             DisplayMode.REFRESH_RATE_UNKNOWN &&
		             mode2.getRefreshRate() !=
		             DisplayMode.REFRESH_RATE_UNKNOWN &&mode1.getRefreshRate() != mode2.getRefreshRate())
	          {
	              return false;
	          }

	          return true;
	     }
   
   /** Returns the width of the frame
    * 
    * @return the width of the frame
    */
   
	public static double getWindowX() 
	{
		return j.getX();
	}

	/** Returns the height of the frame
	 * 
	 * @return the height of the frame
	 */
	
	public static double getWindowY() 
	{
		return j.getY();
	}

	/** Returns the next map to be loaded
	 * 
	 * @return the next map to be loaded
	 */
	
	public static Map nextMap() 
	{
		return inputEngine.loadNextMap();
	}
	
	/** Resets the main controller
	 * 
	 */
	
	public static void restart() 
	{
      gameEngine = new GameEngine(inputEngine.reloadMap());
      state = State.MENU;
      j.setCursor(Cursor.getDefaultCursor());
      displayEngine.displayMenu(Menu.mainMenu);
	}
	
	/** Stops the main controller from running
	 * 
	 */
	
	public static void stop() 
	{
		try 
		{
			j.dispose();
			gameEngine.stopGame();
			gameEngine.join();
		} catch (InterruptedException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/** Returns a state of the game
	 * 
	 * @return a state of the game
	 */
	
	public static State getState()
	{
		return state;
	}

	/** Sets the state of the game
	 * 
	 * @param s the state of the game
	 */
	
	public static void setState(State s) 
	{
		state = s;
	}

	/** Returns the menu the user chose
	 * 
	 * @return the certain menu the user chose
	 */
	
	public static Menu getCurrentMenu()
	{
		switch(state)
		{
		case MENU:
			return Menu.mainMenu; 
		case TUTORIAL_MENU:
			return Menu.tutorialMenu; 
		case HIGHSCORE_MENU:
			return Menu.highscoreMenu; 
		default:
			return null;
		}
	}

	/** Checks for new high scores
	 * 
	 * @param totalPoints the total points the user earned from playing the game
	 */
	
	public static void checkForHighScore(int totalPoints) 
	{
		ArrayList<HighscoreEntry> scores = inputEngine.getHighscores();
		if(scores.size() < 10)
		{
			inputEngine.addHighscoreEntry(name, totalPoints);
		}else
		{
			if(totalPoints > scores.get(9).getScore())
			{
				inputEngine.addHighscoreEntry(name, totalPoints);
			}
		}
	}
}
